<!--
 * Copyright 2010, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->

<!--

Simple realtime demo system with a few basic effects, showing off
synchronization to music. Written to be easy to understand and still
efficient enough.

100% coder art.
100% coder music.
coder code? if JS counts.

-->


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WebGL Blobs</title>
<style>
body {
  width: 100%;
  height: 100%;
  border: 0px;
  padding: 0px;
  margin: 0px;
  background-color: white;
}
canvas {
  background-color: red;
}
canvas.invisible {
  display: none;
}
#controls {
  z-index: 2;
  position: absolute;
  left: 10px;
  bottom: 10px;
  background-color: #FFEE99;
  display: table;
}
#beat {
  display: table-cell;
  font-family: sans-serif;
  margin: 4px;
  padding: 4px;
  border-width: 1px;
  vertical-align: middle;
}
#viewContainer {
  width: 100%;
  height: 100%;
}
.fpsContainer {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  color: gray;
  font-family: sans-serif;
  background-color: rgba(0,0,0,0.5);
  border-radius: 10px;
  padding: 10px;
}
div.title {
  font-weight: bold;
}
</style>
<script src="../khronos/webgl-debug.js"></script>
<script src="../tdl/base.js"></script>
<script src="immediate_sim.js"></script>
<script src="marching_cubes_tables.js"></script>
<script src="marching_cubes.js"></script>
<script>

tdl.require('tdl.fast');
tdl.require('tdl.fps');
tdl.require('tdl.math');
tdl.require('tdl.primitives');
tdl.require('tdl.shader');
tdl.require('tdl.programs');
tdl.require('tdl.log');
tdl.require('tdl.models');
tdl.require('tdl.buffers');
tdl.require('tdl.framebuffers');
tdl.require('tdl.textures');
tdl.require('tdl.webgl');

var gl;
var canvas;
var aspect;

// Use this to refer to the backbuffer as if it were another framebuffer
var backbuffer;
var quad;
var imm;
var g_numBlobs;
var g_resolution;
var g_requestId;

if (!window.Float32Array) {
  // This just makes some errors go away when there is no WebGL.
  window.Float32Array = function() { };
}

// Useful global math constants
var up = new Float32Array([0, 1, 0])

var output = alert
var curBeat = 0
var then = 0.0;
var singleEffect = null
var g_fpsTimer;           // object to measure frames per second;

function mainloop() {
  var BPM = 60.0;
  var timeStart = 0.0;  // at what time does the beat start?

  var timeScale = BPM / 60.0;
  var frameCount = 0;
  var totalFrameCount = 0;

  var fpsElem = document.getElementById("fps");

  function render() {
    var now = (new Date()).getTime() * 0.001;
    var elapsedTime;
    if(then == 0.0) {
      elapsedTime = 0.0;
    } else {
      elapsedTime = now - then;
    }
    then = now;
    frameCount++;
    g_fpsTimer.update(elapsedTime);
    fpsElem.innerHTML = g_fpsTimer.averageFPS;

    aspect = canvas.clientWidth / canvas.clientHeight
    singleEffect.render(null, now, g_numBlobs)
    frameCount++;
    totalFrameCount++;
    g_requestId = requestAnimationFrame(render);
  }

  // Repeatedly run render(), attempt to hold 60 but the demo is
  // framerate independent so we will still keep sync even if we
  // lose frames.
  render();
}

function initializeGraphics() {
  canvas = document.getElementById('render_area');
  gl = tdl.webgl.setupWebGL(canvas);
  if (!gl) {
    return false;
  }

  aspect = canvas.clientWidth / canvas.clientHeight
  backbuffer = tdl.framebuffers.getBackBuffer(canvas)
  imm = new ImmSim()

  // Set some sane defaults.
  gl.disable(gl.BLEND);
  gl.depthFunc(gl.LEQUAL);
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

  return true;
}

function setSetting(elem, id) {
  switch (id) {
  case 0:
  case 1:
  case 2:
  case 3:
    g_numBlobs = Math.pow(10, id);
    for (var ii = 0; ii < 4; ++ii) {
      g_setSettingElements[ii].style.color = "gray";
    }
    elem.style.color = "red";
    break;
  case 4:
  case 5:
  case 6:
  case 7:
  case 8:
    var resolution = {"4":16,"5":24,"6":32,"7":40,"8":48}[id]
    singleEffect = new MarchingCubesEffect(resolution);
    for (var ii = 4; ii < 9; ii++) {
      g_setSettingElements[ii].style.color = "gray"
    }
    elem.style.color = "red"
  }
}

/**
 * Sets up the count buttons.
 */
function setupCountButtons() {
  g_setSettingElements = [];
  for (var ii = 0; ii < 100; ++ii) {
    var elem = document.getElementById("setSetting" + ii);
    if (!elem) {
      break;
    }
    g_setSettingElements.push(elem);
    elem.onclick = function(elem, id) {
      return function () {
        setSetting(elem, id);
      }}(elem, ii);
  }
  setSetting(document.getElementById("setSetting1"), 1);
  setSetting(document.getElementById("setSetting5"), 6);
}

var g_setSettingElements = [];

function setup() {
  if (initializeGraphics()) {
    singleEffect = new MarchingCubesEffect(24);
    setupCountButtons();
    // Kick it off!
    mainloop()
  }
}

window.onload = function() {
  g_fpsTimer = new tdl.fps.FPSTimer();
  canvas = document.getElementById('render_area');

  //canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
  // tell the simulator when to lose context.
  //canvas.loseContextInNCalls(1);

  tdl.webgl.registerContextLostHandler(canvas, handleContextLost);
  tdl.webgl.registerContextRestoredHandler(canvas, handleContextRestored);

  setup();
}

function handleContextLost() {
  tdl.log("context lost");
  cancelAnimationFrame(g_requestId);
}

function handleContextRestored() {
  tdl.log("context restored");
  setup();
}

</script>

</head>

<body>
<div class="fpsContainer">
  <div class="fps">fps: <span id="fps"></div>
  <div class="title">Number of Blobs</div>
  <div id="setSetting0">1</div>
  <div id="setSetting1">10</div>
  <div id="setSetting2">100</div>
  <div id="setSetting3">1000</div>
  <div class="title">Resolution</div>
  <div id="setSetting4">16^3</div>
  <div id="setSetting5">24^3</div>
  <div id="setSetting6">32^3</div>
  <div id="setSetting7">40^3</div>
  <div id="setSetting8">48^3</div>
</div>
<div id="viewContainer">
<canvas id="render_area" width="1024" height="512" style="width: 100%; height: 100%;"></canvas>
</div>
</body>

<script id="spinning_cube_fs" type="x-shader/x-fragment">
  precision mediump float;
  varying vec4 v_color;
  void main(void) {
    gl_FragColor = v_color;
  }
</script>

<script id="spinning_cube_vs" type="x-shader/x-vertex">
  attribute vec3 position;
  attribute vec3 normal;
  attribute vec2 texCoord;

  uniform mat4 u_worldviewproj;

  varying vec4 v_color;

  void main(void) {
    v_color = vec4(position.x + 0.5, position.y + 0.5, position.z + 0.5, 1.0);
    gl_Position = u_worldviewproj * vec4(position, 1.0);
  }
</script>

<script id="marching_cube_fs" type="x-shader/x-fragment">
  precision mediump float;
  varying vec4 v_color;
  varying vec3 v_normal;
  uniform vec4 u_ambientUp;
  uniform vec4 u_ambientDown;
  uniform vec3 u_lightDir;
  uniform vec4 u_lightColor;
  varying vec3 v_eyeDir;
  void main(void) {
    float light = max(0.0, dot(normalize(u_lightDir), v_normal));
    float glare = max(0.0, dot(v_normal, normalize(v_eyeDir + u_lightDir)));
    glare = glare*glare;
    glare = glare*glare;
    glare = glare*glare;
    glare = glare*glare;
    glare = glare*glare;
    glare = glare*glare;
    vec4 ambient = mix(u_ambientDown, u_ambientUp, (v_normal.y + 1.0)/2.0);
    // Gamma correction approximation (sqrt)
    vec4 finalColor = sqrt(ambient + light * u_lightColor + glare);
    finalColor.w = 1.0;
    gl_FragColor = finalColor;
  }
</script>

<script id="marching_cube_vs" type="x-shader/x-vertex">
  attribute vec3 position;
  attribute vec3 normal;
  attribute vec2 texCoord;

  uniform mat4 u_worldviewproj;
  uniform mat4 u_worldview;
  uniform mat4 u_world;

  varying vec4 v_color;
  varying vec3 v_normal;
  varying vec3 v_eyeDir;

  void main(void) {
    v_color = vec4(position.x + 0.5, position.y + 0.5, position.z + 0.5, 1.0);
    v_normal = (u_world * vec4(normalize(normal), 0.0)).xyz;
    v_eyeDir = -normalize((u_worldview * vec4(position, 1.0)).xyz);
    gl_Position = u_worldviewproj * vec4(position, 1.0);
  }
</script>

<script id="blur_fs" type="text/something-not-javascript">
  precision mediump float;
  varying vec2 v_texCoord;
  uniform vec2 blurSize;
  uniform vec4 subtract;
  uniform sampler2D mainSampler;
  void main() {
    vec4 sum = vec4(0.0);
    sum += texture2D(mainSampler, v_texCoord - 4.0 * blurSize) * 0.05;
    sum += texture2D(mainSampler, v_texCoord - 3.0 * blurSize) * 0.09;
    sum += texture2D(mainSampler, v_texCoord - 2.0 * blurSize) * 0.12;
    sum += texture2D(mainSampler, v_texCoord - 1.0 * blurSize) * 0.15;
    sum += texture2D(mainSampler, v_texCoord                 ) * 0.16;
    sum += texture2D(mainSampler, v_texCoord + 1.0 * blurSize) * 0.15;
    sum += texture2D(mainSampler, v_texCoord + 2.0 * blurSize) * 0.12;
    sum += texture2D(mainSampler, v_texCoord + 3.0 * blurSize) * 0.09;
    sum += texture2D(mainSampler, v_texCoord + 4.0 * blurSize) * 0.05;
    gl_FragColor = sum - subtract;
  }
</script>

<script id="radial_vs" type="text/something-not-javascript">
  attribute vec4 position;
  attribute vec2 texCoord;
  varying vec4 v_position;
  varying vec2 v_texCoord0, v_texCoord1, v_texCoord2, v_texCoord3;
  uniform float amount;
  void main() {
    vec2 tc = (position.xy + vec2(1.0, 1.0)) / 2.0;
    // tc.y = 1.0 - tc.y;
    v_texCoord0 = tc;
    v_texCoord1 = 0.5 + (tc-0.5) * (1.0 / (1.0 + amount));
    v_texCoord2 = 0.5 + (tc-0.5) * (1.0 / (1.0 + amount * 0.5));
    v_texCoord3 = 0.5 + (tc-0.5) * (1.0 / (1.0 + amount * 1.5));
    gl_Position = position;
  }
</script>

<script id="radial_fs" type="text/something-not-javascript">
  precision mediump float;
  varying vec2 v_texCoord0, v_texCoord1, v_texCoord2, v_texCoord3;
  uniform sampler2D mainSampler;
  uniform float amount;
  uniform float glow;
  void main() {
    vec4 c1 = texture2D(mainSampler, v_texCoord0);
    vec4 c2 = texture2D(mainSampler, v_texCoord1);
    vec4 c3 = texture2D(mainSampler, v_texCoord2);
    vec4 c4 = texture2D(mainSampler, v_texCoord3);
    gl_FragColor = (c1 + c2 + c3 + c4) * glow / 4.0;
  }
</script>

<script id="copy_fs" type="text/something-not-javascript">
  precision mediump float;
  varying vec2 v_texCoord;
  uniform sampler2D mainSampler;
  void main() {
    gl_FragColor = texture2D(mainSampler, v_texCoord);
  }
</script>

<script id="add_fs" type="text/something-not-javascript">
  precision mediump float;
  varying vec2 v_texCoord;
  uniform sampler2D mainSampler;
  uniform sampler2D secondSampler;
  void main() {
    // TODO: Gamma correct add?
    gl_FragColor = texture2D(mainSampler, v_texCoord) + texture2D(secondSampler, v_texCoord);
  }
</script>

</html>
